package com.i2863.receipt.service.impl;

import com.i2863.receipt.controller.ApiStatus;
import com.i2863.receipt.dao.ActivityRepository;
import com.i2863.receipt.dao.WinnerRepository;
import com.i2863.receipt.dto.SinglePrize;
import com.i2863.receipt.entity.Activity;
import com.i2863.receipt.entity.Prize;
import com.i2863.receipt.entity.Receipt;
import com.i2863.receipt.entity.Winner;
import com.i2863.receipt.exception.ServiceException;
import com.i2863.receipt.service.WinnerService;
import com.i2863.receipt.util.DateUtils;
import com.i2863.receipt.util.PhoneUtil;
import com.i2863.receipt.util.RandomUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.math.BigDecimal;
import java.util.*;

/**
 * Created by Administrator on 2018/6/8.
 */
@Service
public class WinnerServiceImpl implements WinnerService {

    @Resource
    private WinnerRepository winnerRepository;

    @Resource
    private ActivityRepository activityRepository;

    @Override
    public Winner findeOne(Integer id) {
        return winnerRepository.findOne(id);
    }

    @Override
    public void add(Winner winner) {
        winnerRepository.save(winner);
    }

    @Override
    public void del(Integer id) {
        winnerRepository.delete(id);
    }

    @Override
    public void update(Winner winner) {
        winnerRepository.saveAndFlush(winner);
    }

    @Override
    public Page<Winner> findByPhoneNumAndSiteId(final String phoneNum, final Integer siteId, final Integer lastId, Integer pageSize) {
        Pageable pageable = new PageRequest(0,pageSize, Sort.Direction.DESC,"id");//jpa从0开始
        Specification<Winner> specification = new Specification<Winner>() {
            List<Predicate> predicates = new ArrayList<Predicate>(); //所有的断言
            @Override
            public Predicate toPredicate(Root<Winner> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                predicates.add(cb.equal(root.get("site").get("id").as(Integer.class),siteId));
                predicates.add(cb.equal(root.get("winPhoneNum").as(String.class),phoneNum));
                if(lastId!=0){//如果lastId为0  则就查询第一页的数据，如果不为0则加上是否小于lastId的条件  然后去10条
                    predicates.add(cb.lessThan(root.get("id").as(Integer.class),lastId));
                }
                return cb.and(predicates.toArray(new Predicate[predicates.size()]));
            }
        };
        return winnerRepository.findAll(specification,pageable);
    }

    /**
     * 报名者信息转换为中奖者信息
     *
     * @param r        报名者
     * @param activity 活动
     * @return 中奖者
     */
    public Winner trans(Receipt r, Activity activity) {
        Date now = new Date();
        Winner winner = new Winner();
        winner.setWinPhoneNum(r.getPhoneNum());
        winner.setReceiptCode(r.getCode());
        winner.setReceiptCodeNum(r.getCodeNum());
        winner.setReceiptMoney(r.getMoney());
        winner.setReceiptDate(r.getReceiptDate());
        winner.setActivity(activity);
        winner.setSite(activity.getSite());
        winner.setCreateDate(now);
        winner.setModifyDate(now);
        return winner;
    }

    @Override
    @Transactional
    public List<Winner> enroll(Activity activity, List<Receipt> enrollList, List<Receipt> reselectList) {
         List<Winner> result = new ArrayList<Winner>();
        //入围奖项
        List<Integer> enrollIdList = new ArrayList<>(enrollList.size());
        List<Integer> reselectIdList = new ArrayList<>();
        for (Receipt r : enrollList) {
            enrollIdList.add(r.getId());
            Winner winner = trans(r, activity);
            result.add(winner);
        }
        winnerRepository.batchInsert(enrollIdList,activity.getSite().getId(),false);
        //复活奖项
        for (Receipt r : reselectList) {
            reselectIdList.add(r.getId());
        }
        winnerRepository.batchInsert(reselectIdList,activity.getSite().getId(),true);
        //更新状态
        activityRepository.updateActivityStatus(Activity.STARTED, activity.getId());
        //返回入围奖项
        return result;
    }

    @Override
    public List<Winner> enrollList(Integer activityId) {
        return winnerRepository.findByActivityIdAndIsReselection(activityId, false);
    }

    @Override
    @Transactional
    public List<Winner> inside(Integer activityId, List<Winner> winnerList, int insideNum) {
        List<Winner> insideList = RandomUtil.random(winnerList, insideNum);
        List<Integer> idList = new ArrayList<Integer>(insideList.size());
        for (Winner w : insideList) {
            idList.add(w.getId());
        }
        winnerRepository.updateInsideByIds(idList);
        winnerRepository.updateOutSideById(activityId);
        activityRepository.updateActivityStatus(Activity.SECOND_ROUND, activityId);
        return insideList;
    }

    @Override
    public List<Winner> insideList(Integer activityId) {
        return winnerRepository.findByActivityIdAndIsInsideTrue(activityId);
    }

    @Override
    public List<Winner> outsideList(Integer activityId) {
        return winnerRepository.findByActivityIdAndIsInsideFalse(activityId);
    }

    @Override
    public Integer countByActivityIdAndLevelAndIsReselection(Integer activityId, Integer level, boolean isReselection) {
        return winnerRepository.countByActivityIdAndLevelAndIsReselection(activityId, level, isReselection);
    }

    @Override
    public List<Winner> findInsideWinner(Integer activityId) {
        return winnerRepository.findByActivityIdAndIsReselectionFalseAndLevelNotNull(activityId);
    }

    @Override
    public List<Winner> findByOutSideNoPrize(Integer activityId) {
        return winnerRepository.findByActivityIdAndIsReselectionTrueAndLevelIsNull(activityId);
    }

    @Override
    public List<Winner> findByInsideNoPrize(Integer activityId) {
        return winnerRepository.findByActivityIdAndIsInsideTrueAndLevelIsNull(activityId);
    }

    @Override
    public void updatePrizeByIdList(List<Integer> idList, BigDecimal money, Integer level, String levelName,
                                    Integer prize, String prizeName
    ) {
        winnerRepository.updatePrizeByIdList(idList, money, level, levelName, prize, prizeName);
    }

    @Override
    @Transactional
    public List<Map<String,Object>> lottery(Activity activity, Prize prize, int num, boolean isReselection) {
        List<Winner> memberList = null;
        if (isReselection) {
            //查询场外未中奖的人
            memberList = findByOutSideNoPrize(activity.getId());
        } else {
            //查询场内未中奖的人
            memberList = findByInsideNoPrize(activity.getId());
        }
        if(memberList.isEmpty() || memberList.size()<num){
            //人数不够
            throw new ServiceException(ApiStatus.PEOPLE_NOT_ENOUGH);
        }
        List<Winner> winnerList = RandomUtil.random(memberList, num);
        List<Integer> winIdList = new ArrayList<Integer>();
        List<Map<String, Object>> result = new ArrayList();
        for (Winner winner : winnerList) {
            winIdList.add(winner.getId());
            Map<String, Object> map = new HashMap<>();
            map.put("receiptCode", winner.getReceiptCode());
            map.put("receiptCodeNum", winner.getReceiptCodeNum());
            map.put("isReselection", winner.getIsReselection());
            map.put("isInside", winner.getIsInside());
            map.put("receiptMoney", winner.getReceiptMoney());
            map.put("receiptDate", DateUtils.format(winner.getReceiptDate(),DateUtils.DATE));
            map.put("winPhoneNum", PhoneUtil.hide(winner.getWinPhoneNum()));
            //获奖情况
            map.put("money", prize.getMoney());
            map.put("level", prize.getLevel());
            map.put("levelName", prize.getName());
            map.put("prize", prize.getPrize());
            map.put("prizeName", prize.getPrizeName());
            result.add(map);
        }
        //写入中奖记录
        updatePrizeByIdList(winIdList, prize.getMoney(), prize.getLevel(), prize.getName(), prize.getPrize(), prize.getPrizeName());
        return result;
    }


    @Override
    @Transactional
    public List<Map<String,Object>> lotteryMulti(Activity activity, List<Prize> prizeList, int num, boolean isReselection) {
        List<Winner> memberList = null;
        //获取中奖级别
        int level = prizeList.get(0).getLevel();
        // 把奖项拆成单个名额
        List<SinglePrize> singlePrizeList = SinglePrize.valueOf(prizeList);
        //查询已经中了此奖项的人
        List<Winner> winnerList = winnerRepository.findByActivityIdAndIsReselectionAndLevel(activity.getId(), isReselection, level);
        //把已有中奖的人排除
        for (Winner winner : winnerList) {
            for (int i = 0; i < singlePrizeList.size(); i++) {
                SinglePrize singlePrize = singlePrizeList.get(i);
                if (winner.getLevel().equals(singlePrize.getLevel()) && winner.getPrize().equals(singlePrize.getPrize())) {
                    singlePrizeList.remove(i);
                    break;
                }
            }
        }
        //奖项不够
        if (singlePrizeList.size() < num) {
            throw new ServiceException(ApiStatus.PRIZE_NOT_ENOUGH);
        }
        //排序
        Collections.sort(singlePrizeList);
        //查询未中奖的人
        if (isReselection) {
            //场外
            memberList = findByOutSideNoPrize(activity.getId());
        } else {
            //场内
            memberList = findByInsideNoPrize(activity.getId());
        }
        if(memberList.isEmpty() || memberList.size()<num){
            //人数不够
            throw new ServiceException(ApiStatus.PEOPLE_NOT_ENOUGH);
        }
        //产生中奖者
        List<Winner> winList = RandomUtil.random(memberList, num);
        List<Map<String,Object>> result = new ArrayList<>();
        for (int i = 0; i < winList.size(); i++) {
            SinglePrize singlePrize = singlePrizeList.get(i);
            Winner w = winList.get(i);
            Map<String, Object> map = new HashMap<>();
            map.put("receiptCode", w.getReceiptCode());
            map.put("receiptCodeNum", w.getReceiptCodeNum());
            map.put("isReselection", w.getIsReselection());
            map.put("isInside", w.getIsInside());
            map.put("receiptMoney", w.getReceiptMoney());
            map.put("receiptDate", DateUtils.format( w.getReceiptDate(),DateUtils.DATE));
            map.put("winPhoneNum", PhoneUtil.hide(w.getWinPhoneNum()));
            //获奖情况
            map.put("money", singlePrize.getMoney());
            map.put("level", singlePrize.getLevel());
            map.put("levelName", singlePrize.getLevelName());
            map.put("prize", singlePrize.getPrize());
            map.put("prizeName", singlePrize.getPrizeName());
            result.add(map);
            //写入中奖记录
            winnerRepository.updatePrizeById(w.getId(), singlePrize.getMoney(), singlePrize.getLevel(), singlePrize.getLevelName(), singlePrize.getPrize(), singlePrize.getPrizeName());
        }
        return result;
    }

    @Override
    public Page<Winner> queryByPage(final String queryContent, final String startTime, final String endTime,
                                    final Integer activityId, Integer pageNum, Integer pageSize, final Integer siteId,final Integer prizeLevel) {
        //构建分页工具
        Pageable pageable = new PageRequest(pageNum-1,pageSize, Sort.Direction.DESC,"receiptDate");

        Specification<Winner> specification = new Specification<Winner>() {
            //断言集合
            List<Predicate> predicates = new ArrayList<Predicate>();
            @Override
            public Predicate toPredicate(Root<Winner> root, CriteriaQuery<?> query, CriteriaBuilder cb) {

                if(StringUtils.isNotBlank(queryContent)){ //添加断言
                    Predicate likeName = cb.like(root.get("winPhoneNum").as(String.class),"%"+queryContent+"%");
                    predicates.add(likeName);
                }
                if(StringUtils.isNotBlank(startTime)){
                    Predicate start = cb.greaterThanOrEqualTo(root.get("receiptDate").as(String.class),startTime);
                    predicates.add(start);
                }
                if(StringUtils.isNotBlank(endTime)){
                    Predicate end = cb.lessThanOrEqualTo(root.get("receiptDate").as(String.class),endTime);
                    predicates.add(end);
                }
                if(activityId!=null){
                    Predicate activity = cb.equal(root.get("activity").get("id").as(Integer.class),activityId);
                    predicates.add(activity);
                }
                if(prizeLevel!=null){
                    Predicate activity = cb.equal(root.get("level").as(Integer.class),prizeLevel);
                    predicates.add(activity);
                }
                Predicate site = cb.equal(root.get("site").get("id").as(Integer.class),siteId);
                predicates.add(site);
                Predicate money = cb.isNotNull(root.get("money").as(BigDecimal.class));
                predicates.add(money);
                return cb.and(predicates.toArray(new Predicate[predicates.size()]));
            }
        };
        return winnerRepository.findAll(specification,pageable);
    }

    @Override
    public List<Winner> queryList(final String startTime, final String endTime,
                                  final Integer activityId, final String queryContent, final Integer siteId,final Integer prizeLevel) {
        Specification<Winner> specification = new Specification<Winner>() {

            List<Predicate> predicates = new ArrayList<Predicate>();
            @Override
            public Predicate toPredicate(Root<Winner> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder cb) {
                if(StringUtils.isNotBlank(queryContent)){ //添加断言
                    Predicate likeName = cb.like(root.get("winPhoneNum").as(String.class),"%"+queryContent+"%");
                    predicates.add(likeName);
                }
                if(StringUtils.isNotBlank(startTime)){
                    Predicate start = cb.greaterThanOrEqualTo(root.get("receiptDate").as(String.class),startTime);
                    predicates.add(start);
                }
                if(StringUtils.isNotBlank(endTime)){
                    Predicate end = cb.lessThanOrEqualTo(root.get("receiptDate").as(String.class),endTime);
                    predicates.add(end);
                }
                if(activityId!=null){
                    Predicate activity = cb.equal(root.get("activity").get("id").as(Integer.class),activityId);
                    predicates.add(activity);
                }
                if(prizeLevel!=null){
                    Predicate activity = cb.equal(root.get("level").as(Integer.class),prizeLevel);
                    predicates.add(activity);
                }
                Predicate site = cb.equal(root.get("site").get("id").as(Integer.class),siteId);
                predicates.add(site);
                return cb.and(predicates.toArray(new Predicate[predicates.size()]));
            }
        };
        return winnerRepository.findAll(specification);
    }

    @Override
    public List<Winner> findByActivityIdAndLevel(Integer activityId, Integer level){
        return  winnerRepository.findByActivityIdAndLevel(activityId,level);
    }

    @Override
    public List<Winner> findByActivityIdAndLevelAndIsReselection(Integer activityId, Integer level, boolean isReselection) {
        return winnerRepository.findByActivityIdAndLevelAndIsReselection(activityId,level,isReselection);
    }

    @Transactional
    @Override
    public void updateWinner(Winner winner) {
        winnerRepository.updateWinnerData(winner.getLevel(),winner.getLevelName(),winner.getMoney(),
                winner.getPrize(),winner.getPrizeName(),
                winner.getReceiptCode(),winner.getActivity().getId());
    }

    @Override
    public void del(List<Winner> list) {
        winnerRepository.delete(list);
    }

    @Override
    public Winner findWinnerByActivity_IdAndReceiptCode(String receiptCode, Integer activityId) {
        return winnerRepository.findByActivityAndCode(receiptCode,activityId);
    }
}
